<?php
/**
 * Use session_set_save_handler to save sessions in Oracle.
 * Set up a table:
 *
 *     -- PHPSESSIONS
 *     CREATE TABLE PHPSESSIONS (
 *         SESSION_KEY  VARCHAR2(32)  NOT NULL,
 *         SESSION_EXP  DATE          NOT NULL,
 *         SESSION_VAL  CLOB,
 *         PRIMARY KEY(SESSION_KEY)
 *     );
 */
class SessionHandlerOCI extends Spaph_Plugin {
    public $OCI_SESSION_MAXLIFETIME = null;
    public $OCI_SESSION_TABLE       = 'PHPSESSIONS';

    public function __construct()
    {
        $this->OCI_SESSION_MAXLIFETIME = get_cfg_var("session.gc_maxlifetime");

        session_set_save_handler(array($this, 'oci_session_open'),
                                 array($this, 'oci_session_close'),
                                 array($this, 'oci_session_read'),
                                 array($this, 'oci_session_write'),
                                 array($this, 'oci_session_destruct'),
                                 array($this, 'oci_session_gc'));
    }

    /**
     * creates an ocilogin connection.
     */
    public function oci_session_open($save_path, $session_name)
    {
       return true;
    }

    /**
     * closes an ocilogin connection.
     */
    public function oci_session_close()
    {
        return true;
    }

    /**
     * reads a session key then calls data from phpsessions table.
     */
    function oci_session_read($key) {
        $OCI_SESSION_TABLE = $this->OCI_SESSION_TABLE;

$sql1 = <<< End_SQL
SELECT SESSION_VAL FROM $OCI_SESSION_TABLE WHERE SESSION_KEY = :SESSION_KEY AND SESSION_EXP > SYSDATE
End_SQL;

        $conn = Spaph::getInstance()->dbh()->getConnection();
        $stmt = oci_parse($conn, $sql1);

        oci_bind_by_name($stmt, ':SESSION_KEY', $key, -1);
        oci_execute($stmt);
        oci_fetch_all($stmt, $results, null, null, OCI_ASSOC+OCI_FETCHSTATEMENT_BY_ROW);
        oci_free_statement($stmt);

        if(isset($results[0]['SESSION_VAL']))
            return $results[0]['SESSION_VAL'];

        return '';
    }

    /**
     * oci_session_write()
     *
     * will insert a new or update an existing session.
     */
    public function oci_session_write($key, $val)
    {
        $this->_oci_session_exists($key) ?
                $this->_oci_session_update($key, $val) : $this->_oci_session_insert($key, $val);

        return true;
    }

    /**
     * Check if a session key exists in the phpsessions table.
     *
     * @param string $key
     * @return boolean
     */
    private function _oci_session_exists($key)
    {
        $OCI_SESSION_TABLE = $this->OCI_SESSION_TABLE;

$sql1 = <<< End_SQL_ONE
SELECT SESSION_KEY FROM $OCI_SESSION_TABLE WHERE SESSION_KEY = :B_SESSION_KEY
End_SQL_ONE;

        $conn = Spaph::getInstance()->dbh()->getConnection();
        $stmt = oci_parse($conn, $sql1);
        oci_bind_by_name($stmt, ':B_SESSION_KEY', $key, -1);
        oci_execute($stmt);
        oci_fetch_all($stmt, $results, null, null, OCI_ASSOC+OCI_FETCHSTATEMENT_BY_ROW);
        oci_free_statement($stmt);

        if(isset($results[0]['SESSION_KEY']))
            return $results[0]['SESSION_KEY'];

         return '';
    }

    /**
     * Inserts a new session into phpsessions tables.
     */
    private function _oci_session_insert($key, &$val)
    {
        $OCI_SESSION_MAXLIFETIME = $this->OCI_SESSION_MAXLIFETIME;
        $OCI_SESSION_TABLE       = $this->OCI_SESSION_TABLE;

        $days = round($OCI_SESSION_MAXLIFETIME / 86400, 6);

$sql1 = <<< End_SQL_TWO
INSERT INTO $OCI_SESSION_TABLE (SESSION_KEY, SESSION_EXP, SESSION_VAL) VALUES (:SESSION_KEY, SYSDATE+($days), :SESSION_VAL)
RETURNING SESSION_VAL INTO :SESSION_VAL
End_SQL_TWO;

        $conn = Spaph::getInstance()->dbh()->getConnection();
        $stmt = oci_parse($conn, $sql1);

        oci_bind_by_name($stmt, ':SESSION_KEY', $key);
        oci_bind_by_name($stmt, ':SESSION_VAL', $val);
        oci_execute($stmt, OCI_DEFAULT);

        // Commit transaction
        $committed = oci_commit($conn);

        // Test whether commit was successful. If error occurred, return error message
        if (!$committed) {
            $error = oci_error($conn);
            throw new Exception('Commit failed. Oracle reports: ' . $error['message']);
        }

        oci_free_statement($stmt);
    }

    /**
     * Update a session in phpsessions tables.
     */
    private function _oci_session_update($key, &$val)
    {
        $OCI_SESSION_MAXLIFETIME = $this->OCI_SESSION_MAXLIFETIME;
        $OCI_SESSION_TABLE       = $this->OCI_SESSION_TABLE;

        $days = round($OCI_SESSION_MAXLIFETIME / 86400, 6);

$sql1 = <<< End_SQL_TWO
UPDATE $OCI_SESSION_TABLE SET SESSION_EXP = SYSDATE+($days), SESSION_VAL = :SESSION_VAL
WHERE SESSION_KEY = :SESSION_KEY
End_SQL_TWO;

        $conn = Spaph::getInstance()->dbh()->getConnection();
        $stmt = oci_parse($conn, $sql1);

        oci_bind_by_name($stmt, ':SESSION_KEY', $key);
        oci_bind_by_name($stmt, ':SESSION_VAL', $val);
        oci_execute($stmt, OCI_DEFAULT);

        // Commit transaction
        $committed = oci_commit($conn);

        // Test whether commit was successful. If error occurred, return error message
        if (!$committed) {
            $error = oci_error($conn);
            throw new Exception('Commit failed. Oracle reports: ' . $error['message']);
        }

        oci_free_statement($stmt);
    }

    /**
     * Delete session data from phpsessions tables.
     */
    public function oci_session_destruct($key)
    {
        $OCI_SESSION_TABLE = $this->OCI_SESSION_TABLE;

$sql1 = <<< End_SQL_ONE
DELETE FROM $OCI_SESSION_TABLE WHERE SESSION_KEY = :SESSION_KEY
End_SQL_ONE;

        $conn = Spaph::getInstance()->dbh()->getConnection();
        $stmt = oci_parse($conn, $sql1);
        oci_bind_by_name($stmt, ':SESSION_KEY', $key);
        oci_execute($stmt);
        oci_free_statement($stmt);
    }

    /**
     * Cleans old session from phpsessions tables.
     */
    public function oci_session_gc($maxlifetime)
    {
        $OCI_SESSION_TABLE = $this->OCI_SESSION_TABLE;

$sql1 = <<< End_SQL_ONE
DELETE FROM $OCI_SESSION_TABLE WHERE SESSION_EXP < SYSDATE
End_SQL_ONE;

        $conn = Spaph::getInstance()->dbh()->getConnection();
        $stmt = oci_parse($conn, $sql1);
        oci_execute($stmt);
        oci_free_statement($stmt);

        return true;
    }

} // End class